#include "Global.h"

//全局信息定义
map<string,int> m_usersocket;
map<string,string> m_redis;
map<string,set<string>> m_group;
map<string,string> m_user_group;

//mysql 
static const char* host = "127.0.0.1";
static const char* un = "root";
static const char* pw = "123";
static const char* database_name = "USERT";
static const int port = 3306;
static const int poolSize = 5;

MySQLConnectionPool mysqlconnectionpool( host, un, pw,  database_name, port, poolSize);

mutex mutex_usersock;
mutex mutex_group;
mutex mutex_sql;

//mysql 登录读数据 
string GL::ReadByUsername(string username)
{
    MYSQL* conn = mysqlconnectionpool.getConnection();
    string str_sql = "select password from users where username='" +  username +"';";
    string str_res ="";
      // 执行SQL查询语句  
    if (mysql_query(conn, str_sql.c_str())) {  
        perror("mysql_query");
        return " ";
    }
    
    MYSQL_RES *res;  
    MYSQL_ROW row;
     // 获取结果集  
    res = mysql_store_result(conn);  
      // 检查是否有结果集  
    if (res == NULL) {  
        perror("mysql_query");
        return " ";
    }  
      // 获取列数
    
    if(mysql_num_rows(res) > 0 )
    {
        row = mysql_fetch_row(res);
        str_res = row[0];
        return str_res;
    }

        // 释放结果集  
    mysql_free_result(res); 
    return str_res;
}
    //写用户 需要确保 用户名唯一
bool GL::WriteUsername(string username,string password)
{
    unique_lock<mutex> lock(mutex_sql);
    if(GL::ReadByUsername(username) == "") //user not exist
    {
        MYSQL* conn = mysqlconnectionpool.getConnection();
        string str_sql = "INSERT INTO users (username, password) VALUES ('"+ username +"', '"+ password +"')";
        mysql_query(conn, str_sql.c_str());
        return true;
    }
    return false;
}

    //处理消息
    //每次携带什么 判断是身份
void GL::serverHandle(int soc,string msg)
{
    string str_msgtype = GL::GetValueBuUrl(msg,"type");
    pair<string,int> info = GL::GetLoginStatus(soc);

    if( str_msgtype == "reg" ) //注册 type=reg&username=xxx&password=xxx
    {
        string str_user = GL::GetValueBuUrl(msg,"username");
        string str_pwd = GL::GetValueBuUrl(msg,"password");
        //cout << "registering request u:" << str_user << " p:" << str_pwd << endl;
        bool b_ret = GL::WriteUsername(str_user,str_pwd);
        if(b_ret){
            SUCCESS(soc,"<注册成功>");
        }
        else{
            FAILED(soc,"<注册失败>");
        }
    }else if( str_msgtype == "log") //登录操作 type=log&suername=xxx&password=xxxx&cookie=xxxx
    {
        string str_user = GL::GetValueBuUrl(msg,"username");
        string str_pwd = GL::GetValueBuUrl(msg,"password");
        string str_cookie =  GL::GetValueBuUrl(msg,"cookie");

        RedisClient rediscli;
        string str_commd = "HGET " + str_user + "  session";
        string str_redisres = rediscli.sendCommand( str_commd );
        
        //cout << "redis cookie : " << str_redisres << endl;
        //首先排查 用户cookie 是否存在，存在则直接登录
        if( str_redisres != "nil" ) //存在
        {
            //判断cookie正确性
            if( str_redisres == str_cookie)
            {
                cout << str_redisres << "  :  " << str_cookie << endl;
                //添加用户信息
                unique_lock<mutex> lock(mutex_usersock);
                m_usersocket[str_user] = soc;

                SUCCESS(soc,"<Cookie 登录成功>");
                return;
            }   
        }
        //正常登录
        //cout << "read pwd..." << endl;
        string str_db_pas = GL::ReadByUsername(str_user);
        if( str_db_pas == "" )
        {
            //用户不存在
            FAILED(soc,"<用户不存在>");
        }else
        {
            if( str_db_pas == str_pwd )
            {
                //保存用户socket
                unique_lock<mutex> lock(mutex_usersock);
                m_usersocket[str_user] = soc;
                //更新redis 登录信息随机生产cookie 设置redis生存时间周期
                string str_gen_cookie = GL::GenCookie();
                string str_store_cookie = str_user + ":" + str_gen_cookie;
                RedisClient rediscli;
                rediscli.sendCommand("HSET "+str_user + "  session  " + str_gen_cookie + "  EX 3600"); // ex 1h
      

                SUCCESS(soc,str_store_cookie);
            }else
            {
                FAILED(soc,"<密码错误或者登录信息过期>");
            }
        }
        //cout << "login finish.." << endl;
    }else if( str_msgtype == "get" && info.second != -1)  //获取在线用户  type=get&param=USERLIST
    {
        string str_type = GL::GetValueBuUrl(msg,"param");
        if( str_type == "USERLIST")
        {
            string str_ul = "在线用户:\n";
            for( map<string,int>::const_iterator iter = m_usersocket.begin() ; iter != m_usersocket.end() ; ++iter)
            {
                str_ul = str_ul + iter->first + "\n";
            }
            SUCCESS(soc,str_ul);
        }else
        {
            FAILED(soc,"<获取无效>");
        }
    }else if( str_msgtype == "chat" && info.second != -1) //私聊 type=chat&myself=xxx&target=xxx&msg=xxx
    {
        string str_myself = info.first;//GL::GetValueBuUrl(msg,"myself");
        string str_target =  GL::GetValueBuUrl(msg,"target");
        string str_msg =  GL::GetValueBuUrl(msg,"msg");
        if( m_usersocket.find(str_myself) == m_usersocket.end() ) //
        {
            //未登录
            FAILED(soc,"<当前未登录>");
        }else
        {
            //判断对方是否登录
            if( m_usersocket.find(str_target) != m_usersocket.end() )
            {
                string str_msg2 = str_msg + "    from:" +str_myself;
                
                SEND(m_usersocket[str_target],str_msg2);
                if(str_msg == "exit")
                {
                    SEND(m_usersocket[str_myself],str_msg);
                }
            }else
            {
                FAILED(soc,"<对方未登录>");
            }
        }
    }else if( str_msgtype == "ght" && info.second != -1) //群聊  创建、获取、加入群聊\quit group  ght:create  ght:get  ght:join 123(群号) ght:q type=ght&c=123&param=create 
    {
        string str_myself = info.first;//GL::GetValueBuUrl(msg,"myself");
        string str_cmd = GL::GetValueBuUrl(msg,"param");
        string str_c = GL::GetValueBuUrl(msg,"c");
        if( str_cmd == "create")
        {
            //生产随机UUID 作为群聊id
            uint64_t ui_gid = GL::GetGid();
            ostringstream ooss;
            ooss << ui_gid;
            //创建群聊 并自动加入
            set<string> set_temp = {str_myself};
            unique_lock<mutex> lock(mutex_group);
            m_group[ooss.str()] = set_temp;
            
            SUCCESS(soc,ooss.str());
        }else if(str_cmd == "get")
        {
            //获取群聊
            string str_gid_list = "群聊ID:\n";
            
            for(map<string,set<string>>::const_iterator iter = m_group.begin() ; iter != m_group.end() ; ++iter)
            {
                str_gid_list = str_gid_list + iter->first + "\n";
            }
            SUCCESS(soc,str_gid_list);
        }else if( str_cmd == "join")   //加入群聊
        {
            if( m_group.find( str_c ) != m_group.end())
            {
                //加入
                m_group[str_c].insert(str_myself);
                
                SUCCESS(soc,"<加入群聊成功>");
            }else
            {
                FAILED(soc,"<群组不存在>");
            }
        }else if( str_cmd == "q")
        {
            if( m_group.count( str_c ) != 0 )
            {
                //shan chu
                for (std::map<std::string, std::set<std::string>>::iterator it = m_group.begin(); it != m_group.end(); ++it) {  
                    it->second.erase(it->first); 
                } 
                
                SUCCESS(soc,"<退出群聊成功>");
            }else
            {
                FAILED(soc,"<群组不存在>");
            }
        }
    }else if( str_msgtype == "cgt" && info.second != -1) //群聊  type=cgt&myself=xxx&gid=xxx&msg=xxx
    {
        string str_myself = info.first;//GL::GetValueBuUrl(msg,"myself");
        string str_gr = GL::GetValueBuUrl(msg,"gid");
        string str_msg = GL::GetValueBuUrl(msg,"msg");

        
        //群聊存在则循环发送消息 并且 用户存在该群聊
        if( m_group[str_gr].find(str_myself)  !=  m_group[str_gr].end() )
        {
            string str_g_msg = str_msg + "   from " + str_myself +"[G" + str_gr + "]";
            //退出请求，则只向对应用户发送
            if( str_msg == "exit")
            {
                SEND(m_usersocket[str_myself],str_g_msg);
            }else{

            for(set<string>::iterator iter = m_group[str_gr].begin(); iter != m_group[str_gr].end() ; ++iter)
            {
                if( str_myself == *iter)
                {
                    continue;
                }
                SEND(m_usersocket[*iter],str_g_msg);
            }

            }
        }else
        {
            FAILED(soc,"<未加入该群聊>");
        }
    }
    else
    {
        FAILED(soc,"<请求无效>");
    }
}
    //URL参数解析
string GL::GetValueBuUrl(const string &param,string key)
{
    vector<string> vec_value;
    GL::Split(param,'&',vec_value);
    vector<string> vec_temp;
    for(size_t i = 0; i < vec_value.size() ; i++)
    {
        vec_temp.clear();
        GL::Split(vec_value[i],'=',vec_temp);
        if( 0 == key.compare(vec_temp[0]))
        {
            return vec_temp[1];
        }
    }
    return "";
}
    //URL参数串构造
string GL::GenUrlParamString(const map<string,string> &map_param)
{
    string str_url = "";
    map<string,string>::const_iterator iter = map_param.cbegin();
    for( iter ; iter != map_param.cend() ; ++iter)
    {
        str_url += iter->first + "=" + iter->second + "&";
    }
    if( str_url.size() != 0)
        str_url.pop_back();
    return str_url;
}

void GL::Split(const string& str, char _type,vector<string> &vec_ret)
{
    size_t i_start = 0,i_end = 0;
    for(size_t i = 0;i < str.size(); i++)
    {
        if( str[i] == _type || (i == str.size() - 1) )
        {
            i_end = i;
            size_t i_len = ( i== str.size() -1) ? (i_end-i_start+1) : (i_end-i_start);
            vec_ret.push_back( str.substr(i_start,i_len));
            i_start = i_end + 1;
        }
    }
}

//cookie
string GL::GenCookie()
{
    random_device rand_dev;
    mt19937 m(rand_dev());
    time_t t = chrono::system_clock::to_time_t(chrono::system_clock::now());
    ostringstream ooss;
    ooss << t << m();
    return ooss.str();

}

//gid
atomic<uint64_t> GL::atomic_gid{0};
uint64_t GL::GetGid()
{
    return ++GL::atomic_gid;
}

pair<string,int> GL::GetLoginStatus(int conn)
{
    map<string,int>::const_iterator  iter = m_usersocket.begin();
    for( iter ; iter != m_usersocket.end(); ++iter)
    {
        if( iter->second == conn)
            return make_pair(iter->first,iter->second);
    }
    return make_pair("",-1);
}